﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using webget.WebApp.Extensions;
/*
 * TODO: Caching of content, check header in GET for last update
 * TODO: Parameter validation
 * TODO: Images, gallery 
 */
namespace webget.WebApp
{
	public partial class proxy : System.Web.UI.Page
	{
		protected void Page_Load(object sender, EventArgs e)
		{
			Parameters parameters = new Parameters(this.Request);

			if (!parameters.IsValid)
			{
				this.OutputHelp();
				return;
			}

			this.Response.Clear();

			//"http://www.blogtalkradio.com/v-radio.rss"

			// Create a request for the URL. 		
			WebRequest request = WebRequest.Create(parameters.Url);
			// If required by the server, set the credentials.
			request.Credentials = CredentialCache.DefaultCredentials;

			// Get the response.
			HttpWebResponse response = (HttpWebResponse)request.GetResponse();

			if (parameters.HasMimeType)
			{
				this.Response.AddHeader("Content-type", Tables.MimeTypes[parameters.MimeType]);
			}
			else
			{
				this.Response.AddHeader("Content-type", response.ContentType);
			}

			if (parameters.HasForceDownload)
			{
				string name = Path.GetFileName(parameters.Url);
				this.Response.AddHeader("content-disposition", "attachment; filename=" + name);
			}

			this.Response.StatusDescription = response.StatusDescription;

			// Get the stream containing content returned by the server.
			Stream dataStream = response.GetResponseStream();
			// Open the stream using a StreamReader for easy access.
			StreamReader reader = new StreamReader(dataStream);
			// Read the content.
			string responseFromServer = reader.ReadToEnd();
			// Display the content.

			if (parameters.HasFilter)
			{
				this.Response.Write(this.CreateOutputByExtension(responseFromServer, parameters));
			}
			else
			{
				this.Response.Write(responseFromServer);
			}

			// Cleanup the streams and the response.
			reader.Close();
			dataStream.Close();
			response.Close();
		}

		#region By Extension

		private string CreateOutputByExtension(string text, Parameters parameters)
		{
			if (parameters.MimeType == Tables.Mime_Html)
			{
				HtmlBuilder hb = new HtmlBuilder();
				hb.beginHtml();
				hb.beginHead();
				// TODO: analytics
				//hb.write(Resources.Resources.Analytics);
				hb.endHead();
				hb.beginBody();

				List<string> list = this.GetByExtension(text, parameters.Filter);
				foreach (string s in list)
				{
					string href = this.GetCleanUrl(new Parameters()
					{
						Url = s,
						ForceDownload = parameters.ForceDownloadOnContent
					});
					hb.write("<a href=\"" + href + "\">" + s + "</a>");
					hb.newLine();
				}

				hb.endBody();
				hb.endHtml();

				return hb.StringBuilder.ToString();
			}
			else
			{
				StringBuilder hb = new StringBuilder();
				List<string> list = this.GetByExtension(text, parameters.Filter);
				foreach (string s in list)
				{
					string href = this.GetCleanUrl(new Parameters()
					{
						Url = s,
						ForceDownload = parameters.ForceDownloadOnContent
					});
					hb.AppendLine(href);
				}
				return hb.ToString();
			}
		}

		private List<string> GetByExtension(string text, string filter)
		{
			List<string> extensions = Tables.ResourceTypes[filter].ToList();
			List<string> list = new List<string>();
			Regex regx = new Regex("http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);

			foreach (Match match in regx.Matches(text))
			{
				foreach (string extension in extensions)
				{
					if (match.Value.EndsWith(extension))
					{
						list.Add(match.Value);
					}
				}
			}
			return list.Distinct().ToList();
		}

		#endregion

		private string GetCleanUrl(Parameters parameters)
		{
			if (!parameters.ForceDownload.IsTrue())
			{
				return parameters.Url;
			}

			string pageName = "proxy.aspx";
			int pageIndex = this.Request.Url.AbsoluteUri.IndexOf("proxy.aspx") + pageName.Length;

			string url = this.Request.Url.AbsoluteUri.Substring(0, pageIndex);
			if (parameters.HasUrl)
			{
				url = url + "?url=" + parameters.Url;
			}
			if (parameters.HasMimeType)
			{
				url = url + "&t=" + parameters.MimeType;
			}
			if (parameters.HasForceDownload)
			{
				url = url + "&f=1";
			}
			if (parameters.HasFilter)
			{
				url = url + "&e=" + parameters.Filter;
			}

			return url;
		}

		private string GetRootUrl()
		{
			int pageIndex = this.Request.Url.AbsoluteUri.IndexOf("proxy.aspx");
			return this.Request.Url.AbsoluteUri.Remove(pageIndex);
		}

		private void OutputHelp()
		{
			string applicationVar = "help";
			HtmlBuilder hb = new HtmlBuilder();

			if (Application[applicationVar] == null)
			{
				hb.beginHtml();
				hb.beginHead();

				string url = GetRootUrl();

				hb.includeStyle(url + "style.css");
				hb.includeScript("http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js");

				hb.includeScript(url + "lib.js");
				// TODO: analytics
				//hb.write(Resources.Resources.Analytics);

				hb.endHead();
				hb.beginBody();

				hb.write("Bugnplay proxy-like tool :)");
				hb.newLine();
				hb.newLine();
				hb.write("<form>");
				hb.bold("Create request:");
				hb.newLine();

				hb.write("<table>");

				hb.table_beginRow();
				hb.table_beginCell();

				hb.write("<label for=\"txtUrl\">Url [url]:</label>");

				hb.table_endCell();
				hb.table_beginCell();

				hb.write("<input type=\"text\" id=\"txtUrl\" style=\"width:400px;\" />");

				hb.table_endCell();
				hb.table_endRow();

				hb.table_beginRow();
				hb.table_beginCell();

				hb.write("<label for=\"txtType\">Content Type [t]:</label>");

				hb.table_endCell();
				hb.table_beginCell();

				hb.write("<select id=\"txtType\">");
				hb.write("<option value=\"\"></option>");
				foreach (KeyValuePair<string, string> keyValue in Tables.MimeTypes)
				{
					hb.write("<option value=\"" + keyValue.Key + "\">" + keyValue.Value + "</option>");
				}
				hb.write("</select>");

				hb.table_endCell();
				hb.table_endRow();

				hb.table_beginRow();
				hb.table_beginCell();

				hb.write("<label for=\"txtForceDownload\">Force Download [f]:</label>");

				hb.table_endCell();
				hb.table_beginCell();

				hb.write("<input type=\"checkbox\" id=\"txtForceDownload\" />");

				hb.table_endCell();
				hb.table_endRow();

				hb.table_beginRow();
				hb.table_beginCell();

				hb.write("<label for=\"txtFilter\">Filter [e]:</label>");

				hb.table_endCell();
				hb.table_beginCell();

				hb.write("<select id=\"txtFilter\">");
				hb.write("<option value=\"\"></option>");
				foreach (KeyValuePair<string, List<string>> keyValue in Tables.ResourceTypes)
				{
					string value = string.Join(",", keyValue.Value.ToArray());
					hb.write("<option value=\"" + keyValue.Key + "\">" + value + "</option>");
				}
				hb.write("</select>");

				hb.table_endCell();
				hb.table_endRow();

				hb.table_beginRow();
				hb.table_beginCell();

				hb.write("<label for=\"txtForceDownloadOnContent\">Force download of filtered content [fi]:</label>");

				hb.table_endCell();
				hb.table_beginCell();

				hb.write("<input type=\"checkbox\" id=\"txtForceDownloadOnContent\" />");

				hb.table_endCell();
				hb.table_endRow();

				hb.write("</table>");

				hb.newLine();
				hb.newLine();
				hb.write("<input type=button id=\"btnCreateUrl\" value=\"Create Url\" />");

				hb.newLine();
				hb.newLine();

				hb.bold("Output:");
				hb.newLine();
				hb.write("<input type=text id=\"txtOutput\"  style=\"width:400px;\" />");
				hb.newLine();
				hb.write("<a href=\"javascript:void(0)\" id=\"linkOutput\" target=\"_blank\"></a>");
				hb.newLine();
				hb.write("</form>");

				hb.newLine();
				hb.newLine();

				//hb.write("Parameters:");
				//hb.newLine();
				//hb.bold("url");
				//hb.separator();
				//hb.write("url of resource");

				//hb.newLine();
				//hb.bold("t");
				//hb.separator();
				//hb.write("override response mime type");

				//hb.newLine();
				//hb.bold("f");
				//hb.separator();
				//hb.write("force download");

				hb.endBody();
				hb.endHtml();

				Application[applicationVar] = hb.StringBuilder.ToString();
			}

			this.Response.Write(Application[applicationVar]);
		}

		#region Paramters

		internal class Parameters
		{
			internal Parameters()
			{
			}

			internal Parameters(HttpRequest request)
				: this()
			{
				if (this.ValidateUrl(request.QueryString["url"]))
				{
					this.Url = request.QueryString["url"];
				}
				else
				{
					return;
				}

				this.MimeType = request.QueryString["t"];

				this.Filter = request.QueryString["e"];

				this.ForceDownload = this.ParseBool(request.QueryString["f"]);
				this.ForceDownloadOnContent = this.ParseBool(request.QueryString["fi"]);
			}

			#region Parameter checker

			internal bool IsValid
			{
				get
				{
					return this.HasUrl;
				}
			}

			internal bool IsEmpty
			{
				get
				{
					return this.HasMimeType
						&& this.HasUrl
						&& this.HasForceDownload
						&& this.HasFilter
						&& this.HasForceDownloadOnContent;
				}
			}

			internal bool HasMimeType
			{
				get
				{
					return !string.IsNullOrEmpty(this.MimeType) && Tables.MimeTypes.ContainsKey(this.MimeType);
				}
			}

			internal bool HasUrl
			{
				get
				{
					return !string.IsNullOrEmpty(this.Url) && this.ValidateUrl(this.Url);
				}
			}

			internal bool HasFilter
			{
				get
				{
					return !string.IsNullOrEmpty(this.Filter) && Tables.ResourceTypes.ContainsKey(this.Filter);
				}
			}

			internal bool HasForceDownload
			{
				get
				{
					return this.ForceDownload.HasValue && this.ForceDownload.Value;
				}
			}

			internal bool HasForceDownloadOnContent
			{
				get
				{
					return this.ForceDownloadOnContent.HasValue && this.ForceDownloadOnContent.Value;
				}
			}

			#endregion

			#region Properties

			[RequestParameter(ParameterName = "url", Type = typeof(string))]
			internal string Url
			{
				get;
				set;
			}

			[RequestParameter(ParameterName = "t", Type = typeof(string))]
			internal string MimeType
			{
				get;
				set;
			}

			[RequestParameter(ParameterName = "f", Type = typeof(bool?))]
			internal bool? ForceDownload
			{
				get;
				set;
			}

			[RequestParameter(ParameterName = "e", Type = typeof(string))]
			internal string Filter
			{
				get;
				set;
			}

			[RequestParameter(ParameterName = "fi", Type = typeof(bool))]
			internal bool? ForceDownloadOnContent
			{
				get;
				set;
			}

			#endregion

			private bool ValidateUrl(string txt)
			{
				if (string.IsNullOrEmpty(txt))
				{
					return false;
				}
				else
				{
					Regex regx = new Regex("http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase);
					return regx.IsMatch(txt);
				}
			}

			private bool ParseBool(string value)
			{
				if (value != null)
				{
					bool force;
					if (bool.TryParse(value, out force))
					{
						return force;
					}
					else if (value == "1")
					{
						return true;
					}
					else
					{
						return false;
					}
				}
				else
				{
					return false;
				}
			}
		}

		[AttributeUsage(AttributeTargets.Property)]
		internal class RequestParameterAttribute : Attribute
		{
			public string ParameterName
			{
				get;
				set;
			}

			public Type Type
			{
				get;
				set;
			}

			//public EventHandler<ValidatorEventArgs> ValidatorHandler
			//{
			//    get;
			//    set;
			//}
		}

		#endregion

		//#region Validator

		//internal class ValidatorEventArgs : EventArgs
		//{
		//    internal object Value
		//    {
		//        get;
		//        set;
		//    }        
		//}

		//internal static class Validator
		//{
		//    internal static bool Validate(object sender, ValidatorEventArgs e)
		//    {
		//        return true;
		//    }
		//}

		//#endregion

		#region Tables

		internal static class Tables
		{
			#region Constants

			internal const string Type_Image = "image";
			internal const string Type_Audio = "audio";
			//internal const string Type_Flash = "flash";

			internal const string Mime_Html = "html";
			internal const string Mime_Xml = "xml";
			internal const string Mime_Json = "json";
			internal const string Mime_Text = "text";

			#endregion

			#region Fields

			private static Dictionary<string, List<string>> m_ResourceTypes;
			private static Dictionary<string, string> m_MimeTypes;

			#endregion

			#region Properties

			internal static Dictionary<string, string> MimeTypes
			{
				get
				{
					if (Tables.m_MimeTypes == null)
					{
						Tables.m_MimeTypes = GetMimeTypes();
					}
					return Tables.m_MimeTypes;
				}
			}

			internal static Dictionary<string, List<string>> ResourceTypes
			{
				get
				{
					if (Tables.m_ResourceTypes == null)
					{
						Tables.m_ResourceTypes = GetResourceTypes();
					}
					return Tables.m_ResourceTypes;
				}
			}

			#endregion

			#region Creation Methods

			private static Dictionary<string, List<string>> GetResourceTypes()
			{
				Dictionary<string, List<string>> dict = new Dictionary<string, List<string>>();

				//dict.Add(Type_Image, new List<string> { "jpg", "jpeg", "bmp", "png" });
				dict.Add(Type_Audio, new List<string> { "mp3", "wav" });
				//dict.Add(Type_Flash, new List<string> { "swf" });

				return dict;
			}

			private static Dictionary<string, string> GetMimeTypes()
			{
				Dictionary<string, string> dict = new Dictionary<string, string>();

				dict.Add(Mime_Html, "text/html");
				dict.Add(Mime_Xml, "application/xml");
				dict.Add(Mime_Json, "application/json");
				dict.Add("text", "text/plain");

				return dict;
			}

			#endregion
		}

		#endregion
	}
}